﻿using System;
using System.Collections.Generic;
using System.Text;
using FunctionalCLR.Core.Function.Flow;

namespace FunctionalCLR.Core.Function {
    public class UserDefinedFunctionNode : FuncNode, IUserDefinedType {

        public string Name {
            get {
                return this.Value;
            }
            set {
                this.Value = value;
            }
        }

        public string FuncBody {
            get;
            set;
        }

        public string ClassName {
            get;
            set;
        }

        public string[] Parameters {
            get;
            set;
        }

        public Node Body {
            get;
            set;
        }

        public string Comment {
            get;
            set;
        }

        public int LineNumber {
            get;
            set;
        }

        public IFunctionManager FunctionManager {
            get;
            set;
        }

        public IParserFactory ParserFactory {
            get;
            set;
        }

        public string GetInternalName(int index) {
            //if (string.IsNullOrEmpty(this.ClassName)) {
            if(index == 0) {
                return this.Name;
            }

            return string.Format(
                "{0}_{1}", this.Name, index);
            //}

            //return string.Format(
            //    "{0}.{1}_{2}", this.ClassName, this.Name, index);
        }

        public override FuncNode GetReversedFunction(int index) {
            string internalName = GetInternalName(index);
            return FunctionManager.Get(internalName);
        }

        public void UpdateParameters() {
            this.Parameters = new string[Children.Count];
            for (int index = 0; index < Children.Count; index++) {
                this.Parameters[index] = (string)Children[index].Evaluate();
            }
        }

        public override object Evaluate() {
            string expr = this.FuncBody;

            for (int index = 0; index < this.Children.Count; index++) {
                object value = Children[index].Evaluate();
                string param = this.Parameters[index];
                expr = Utils.ReplaceString(
                    param, value.ToString(), expr);
            }

            Parser parser = this.ParserFactory.CreateParser(expr);
            Node root = parser.Parse();

            object result = null;

            try {
                result = root.Evaluate();
            } catch (ReturnException ex) {
                result = ex.Result;
            }
            
            return result;
        }

        public bool CheckParameter(string name) {
            foreach (string p in Parameters) {
                if (name == p) {
                    return true;
                }
            }

            return false;
        }

        public bool CheckFunction() {
            if (Body == null) {
                return false;
            }

            return CheckFunction(Body);
        }

        protected bool CheckFunction(Node Body) {
            if (Body is IdentifierNode) {
                if (!CheckParameter(Body.Value)) {
                    return false;
                }

                return true;
            }

            foreach (Node child in ((FuncNode)Body).Children) {
                if (!CheckFunction(child)) {
                    return false;
                }
            }

            return true;
        }
    }
}
